﻿//Copyright (C) <2013>  <jonathan cleeve norton> All Rights Reserved 
//Contact jon.norton@fin-plus.co.uk website <http://www.fin-plus.co.uk/>
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Diagnostics;
using System.ComponentModel;
using FinPlusCommon;
using log = Logger.Logger;
using u = FinPlusUtility.Utility;

namespace FinPlusComponents
{
    public class FinPlusQuoteInstruments : ConcurrentDictionary<string, FinPlusQuoteInstrument>
    {
        public void Dispose()
        {
            foreach (var quoteInstrument in this.Values)
                quoteInstrument.Dispose();
        }
    }

    public class FinPlusQuoteInstrument : FinPlusComponent
    {
        public override string Id { get; set; }
        public override string[] States { get { return new string[] { }; } }
        public override string[] TestStates { get { return new string[] { }; } }
        public override IFinPlusFunctions Model { get { return null; } }
        public override string ModelTest { get { return null; } }
        public override bool IsValid { get; set; }

        public FinPlusQuoteSources Sources { get { return _platforms; } }
        public FinPlusQuoteSource Trader { get { return _trader; } }
        public string Instrument { get; private set; }
        public string InstrumentType { get { return _instrumentType.Value; } }

        private enum Params { Instrument, Id, Settle, End, CollateralCurve, InstrumentType, BookedTrade, ReceiveAction, PublishAction, Trade, VM }

        private Action<IFinPlusFunction> _publish;
        private readonly IFinPlusFunction _instrument;
        private readonly FinPlusQuoteSource _virtual, _trader;
        private readonly FinPlusQuoteSources _platforms;
        private readonly IDictionary<string, string> _counterpartyCollateralCurves;
        private readonly IFinPlusItem _quoteReceiveAction, _quotePublishAction, _bookedTrade, _instrumentType;
        private FinPlusQuoteSpreadCallbacks _spreadCallbacks;

        //construct
        public FinPlusQuoteInstrument(IFinPlusFunction instrument, IDictionary<string, string> CounterpartyCollateralCurves, IList<string> sourceNames, Action<IFinPlusFunction> publish)
        {
            Instrument = instrument[Params.Instrument.ToString()].ToString();
            _counterpartyCollateralCurves = CounterpartyCollateralCurves;
            _publish = publish;
            _instrument = instrument;
            _instrumentType = instrument.Get(Params.InstrumentType.ToString());
            _quoteReceiveAction = instrument.Add(Params.ReceiveAction.ToString(), new Function());
            _quotePublishAction = instrument.Add(Params.PublishAction.ToString(), new Function());
            _quotePublishAction.PropertyChanged += Quote_Publish;
            _bookedTrade = instrument.Add(Params.BookedTrade.ToString(), new Function());

            _platforms = new FinPlusQuoteSources();
            _trader = new FinPlusQuoteSource(instrument, Params.Trade.ToString(), publish, _platforms);
            _virtual = new FinPlusQuoteSource(instrument, Params.VM.ToString());

            _platforms[_virtual.SourceName] = _virtual;
            foreach (var sourceName in sourceNames)
                _platforms[sourceName] = new FinPlusQuoteSource(instrument, sourceName);
        }

        //common control interface
        public override void Dock(object component, string dock)
        {
            IsValid = false;
            throw new NotImplementedException();
        }

        public override void Initialise()
        {

        }

        public override void CommandRouter(IFinPlusFunction func) { }

        public override void Dispose()
        {
            if (_trader != null)
                _trader.Dispose();
        }

        //public
        public void AddSpreads(IFinPlusFunction[] spreads, FinPlusQuoteInstruments quoteInstruments)
        {
            _trader.AddSpreads(spreads, quoteInstruments, this);
        }

        public void AddQuoteInterests(IFinPlusFunction[] quoteInterests)
        {
            _trader.AddQuoteInterests(quoteInterests);
            foreach (var platform in _platforms.Values)
                platform.AddQuoteInterests(quoteInterests);
        }

        public void LinkSpread(string instrument, Action<string, FinPlusQuoteWrapper, string> action)
        {
            if (_spreadCallbacks == null) _spreadCallbacks = new FinPlusQuoteSpreadCallbacks();
            _spreadCallbacks[instrument] = action;
        }

        public void Quote(FinPlusQuoteWrapper quote)
        {
            try
            {
                //for trade parsing
                quote.Quote[Params.InstrumentType.ToString()] = _instrumentType.Value;
                quote.Quote[Params.Settle.ToString()] = _instrument.Get(Params.Settle.ToString()).Object;
                quote.Quote[Params.End.ToString()] = _instrument.Get(Params.End.ToString()).Object;
                quote.Quote.Get(Params.Id.ToString()).Set(_instrument.Id);
 
                //add default collateral curve for OTC
                if (_counterpartyCollateralCurves.ContainsKey(quote.Counterparty))
                    quote.Quote[Params.CollateralCurve.ToString()] = _counterpartyCollateralCurves[quote.Counterparty];

                //apply quote to virtual market
                var virtualHasUpdated = _virtual.Quote(quote, Params.VM.ToString());

                //update best source quote
                bool hasUpdated = false;
                FinPlusQuoteSource quoteSource;
                if (_platforms.TryGetValue(quote.SourceName, out quoteSource))
                    hasUpdated = quoteSource.Quote(quote, quote.SourceName);

                //update trader quote related spreads
                if (_spreadCallbacks != null && (virtualHasUpdated || hasUpdated))
                    foreach (var spreadCallback in _spreadCallbacks.Values)
                        spreadCallback(quote.Instrument, _trader.BestQuote(quote.BuySell), quote.BuySell);
            }
            catch (Exception e)
            {
                Error(e.Message);
            }
        }

        public void QuoteMissed(FinPlusQuoteWrapper quote)
        {
            _quoteReceiveAction.Set(quote.Quote);
        }

        public void QuoteAccept(FinPlusQuoteWrapper quote)
        {
            _quoteReceiveAction.Set(quote.Quote);
        }

        public void QuoteDecline(FinPlusQuoteWrapper quote)
        {
            _quoteReceiveAction.Set(quote.Quote);
        }

        public void QuoteCancel(FinPlusQuoteWrapper quote)
        {
            try
            {
                //apply cancel to virtual market
                var virtualHasUpdated = _virtual.QuoteCancel(quote, Params.VM.ToString());

                //update best source quote
                FinPlusQuoteSource quoteSource;
                bool hasUpdated = false;
                if (_platforms.TryGetValue(quote.SourceName, out quoteSource))
                    hasUpdated = quoteSource.QuoteCancel(quote, quote.SourceName);

                //update trader quote related spreads
                if (_spreadCallbacks != null && (virtualHasUpdated || hasUpdated))
                {
                    foreach (var spreadCallback in _spreadCallbacks.Values)
                    {
                        spreadCallback(quote.Instrument, _trader.BestQuote(quote.BuySell), quote.BuySell);
                    }
                }
            }
            catch (Exception e)
            {
                //managed error
                Error(e.Message);
            }
        }

        public void QuoteExecutePublish(FinPlusQuoteWrapper quote)
        {
            _trader.QuoteExecutePublish(quote);
        }

        public void BookedTrade(IFinPlusFunction trade)//for testing
        {
            _bookedTrade.Set(trade);
        }

        public void Error(string message)
        {
            //switch off trader quotes
            _trader.Error(message);
        }

        //events
        public void Quote_Publish(object sender, PropertyChangedEventArgs a) { _publish(Function.FromString(((IFinPlusItem)sender).Value)); }
    }
}
